Skip to content

Astrophoto stretch#80

Merged
meesoft merged 5 commits into
mainfrom
features/AstroStretch
May 10, 2026
Merged

Astrophoto stretch#80
meesoft merged 5 commits into
mainfrom
features/AstroStretch

Conversation

@meesoft
Copy link
Copy Markdown
Owner

@meesoft meesoft commented Apr 27, 2026

Summary by CodeRabbit

  • New Features

    • Astrophoto stretch mode with adjustable stretch, background smoothing and black point; new UI controls and Ctrl+R/context-menu access.
  • Bug Fixes

    • Improved Sun/Moon position display and formatting.
    • Better metadata/timestamp handling and RAW image support.
  • Refactor

    • Simplified preview loading API.
  • Tests

    • Added unit test covering astro stretch behavior.
  • Chores

    • Test/runtime dependency updates.

Review Change Stack

@meesoft meesoft changed the title Astro stretch Astro stretch feature Apr 27, 2026
@meesoft meesoft changed the title Astro stretch feature Astrophoto stretch May 2, 2026
@meesoft meesoft marked this pull request as ready for review May 10, 2026 12:20
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a89bb196-2d53-45ba-890b-0cc2a2d03fa7

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee1a45 and 8925cee.

📒 Files selected for processing (7)
  • PhotoLocator/BitmapOperations/AstroStretchOperation.cs
  • PhotoLocator/JpegTransformCommands.cs
  • PhotoLocator/LocalContrastViewModel.cs
  • PhotoLocator/MainWindow.xaml
  • PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs
  • PhotoLocatorTest/PhotoLocatorTest.csproj
  • PhotoshopImageLoader/PhotoLocator.PhotoshopImageLoader.csproj
✅ Files skipped from review due to trivial changes (2)
  • PhotoshopImageLoader/PhotoLocator.PhotoshopImageLoader.csproj
  • PhotoLocatorTest/PhotoLocatorTest.csproj
🚧 Files skipped from review as they are similar to previous changes (5)
  • PhotoLocator/JpegTransformCommands.cs
  • PhotoLocator/MainWindow.xaml
  • PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs
  • PhotoLocator/BitmapOperations/AstroStretchOperation.cs
  • PhotoLocator/LocalContrastViewModel.cs

Walkthrough

This PR adds an AstroStretchOperation and integrates an "astro mode" into LocalContrast: new bitmap stretch and optimize logic, enum-driven ViewModel pipeline changes, UI controls and command bindings, raw/video file helpers, metadata/timestamp improvements, preview-loading signature changes, and unit tests.

Changes

Astrophotography Stretch Feature

Layer / File(s) Summary
File Type Detection & Metadata
PhotoLocator/PictureFileFormats/GeneralFileFormatHandler.cs, PhotoLocator/Metadata/ExifHandler.cs, PhotoLocator/Metadata/ExifTool.cs
New IsRawFile() and IsVideoFile() helpers; ExifHandler detects RAW and returns metadata without frame dims; ExifTool adds ModifyDate fallback and DateTime.TryParse fallback.
Bitmap Stretch Operation
PhotoLocator/BitmapOperations/FloatBitmap.cs, PhotoLocator/BitmapOperations/AstroStretchOperation.cs
FloatBitmap inner-loop width caching; new AstroStretchOperation with exponential stretch, optional grayscale + IIR background smoothing, BlackPoint subtraction, and OptimizeStretch() brute-force selection.
Celestial Position API
PhotoLocator/Helpers/CelestialCalculator.cs
GetSunPosition() signature changed from nullable double? to (double Azimuth, double Altitude) tuple (altitude in degrees).
Preview Loading Refactoring
PhotoLocator/PictureItemViewModel.cs, PhotoLocator/JpegTransformCommands.cs
LoadPreviewAsync() removes maxWidth parameter; call sites updated to use the new overload; IsVideo uses IsVideoFile() helper.
ViewModel Infrastructure
PhotoLocator/LocalContrastViewModel.cs
Introduces FirstParamChanged enum, _sourceFloatBitmap, and enum-based StartUpdateTimer/UpdatePreviewAsync ordering to refresh only necessary pipeline stages.
Astro Mode Features
PhotoLocator/LocalContrastViewModel.cs
Adds IsAstroModeEnabled, AstroStretch, BackgroundRemovalSmooth, BlackPoint with reset commands; integrates astro params into save/restore and ApplyOperations via ApplyAstroStretchOperation.
Preview & Operation Pipeline
PhotoLocator/LocalContrastViewModel.cs
ApplyAstroStretchOperation() conditionally runs AstroStretchOperation; UpdatePreviewAsync and ApplyOperations route through _sourceFloatBitmap and the astro stage before Laplacian/tone stages; histogram uses _sourceFloatBitmap.
UI Controls & Bindings
PhotoLocator/LocalContrastView.xaml, PhotoLocator/JpegTransformCommands.cs
Adds BooleanToVisibilityConverter; LocalContrastView shows astro controls (stretch, background smooth, black point) when IsAstroModeEnabled; LocalContrastCommand recognizes "Astro" parameter.
Main Window & Menu
PhotoLocator/MainWindow.xaml
Adds Ctrl+R key binding and "Astrophoto stretch (Ctrl+R)" context menu item invoking LocalContrastCommand with CommandParameter="Astro".
Celestial Display
PhotoLocator/MainViewModel.cs, PhotoLocator/MapDisplay/MapView.xaml
MainViewModel uses new GetSunPosition tuple directly, updates formatting and sun trace color based on altitude; minor whitespace change in MapView.
Unit Tests & Projects
PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs, PhotoLocatorTest/PhotoLocatorTest.csproj, PhotoshopImageLoader/PhotoLocator.PhotoshopImageLoader.csproj
Adds AstroStretchOperation test; updates test SDK/MSTest package versions and System.Drawing.Common package version in PhotoshopImageLoader.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: introducing astrophoto stretch functionality across multiple files including a new AstroStretchOperation class, UI controls, and astrophotography-specific image processing parameters.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch features/AstroStretch

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
PhotoLocator/LocalContrastViewModel.cs (1)

573-589: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include astro settings in the no-op fast path.

ApplyOperations() returns the original source when IsNoOperation is true, but IsNoOperation does not account for IsAstroModeEnabled, AstroStretch, BackgroundRemovalSmooth, or BlackPoint. That means an astro-only edit can preview correctly and still be skipped when the final image is applied/saved.

💡 Suggested fix
-        public bool IsNoOperation => DetailHandling == 1 && ToneMapping == 1 && HighlightStrength == 0 && ShadowStrength == 0 && Contrast == DefaultContrast 
-            && !_colorToneOperation.AreToneAdjustmentsChanged;
+        public bool IsNoOperation =>
+            (!IsAstroModeEnabled || (AstroStretch == 0 && BackgroundRemovalSmooth == 0 && BlackPoint == 0)) &&
+            DetailHandling == 1 &&
+            ToneMapping == 1 &&
+            HighlightStrength == 0 &&
+            ShadowStrength == 0 &&
+            Contrast == DefaultContrast &&
+            !_colorToneOperation.AreToneAdjustmentsChanged;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PhotoLocator/LocalContrastViewModel.cs` around lines 573 - 589, The no-op
fast path (IsNoOperation) currently ignores astro-related settings so
ApplyOperations can incorrectly skip astro-only edits; update the IsNoOperation
getter to also require astro mode and astro parameters be at their
defaults—e.g., include checks on IsAstroModeEnabled (or
_astroOperation.IsAstroModeEnabled), AstroStretch == DefaultAstroStretch,
BackgroundRemovalSmooth == DefaultBackgroundRemovalSmooth and BlackPoint ==
DefaultBlackPoint (or the corresponding properties/fields) so that any
non-default astro setting prevents returning the original source and forces the
astro pipeline (ApplyAstroStretchOperation and related steps) to run.
🧹 Nitpick comments (1)
PhotoLocator/JpegTransformCommands.cs (1)

106-106: ⚡ Quick win

Replace the Astro command magic string with a shared constant.

Line 106 hardcodes "Astro" and relies on matching string literals in XAML. A shared constant reduces drift risk and makes renames safe.

Proposed refactor
--- a/PhotoLocator/JpegTransformCommands.cs
+++ b/PhotoLocator/JpegTransformCommands.cs
@@
 public sealed class JpegTransformCommands
 {
+    public const string AstroCommandParameter = "Astro";
@@
-    localContrastViewModel = new LocalContrastViewModel() { IsAstroModeEnabled = o as string == "Astro", SourceBitmap = image };
+    localContrastViewModel = new LocalContrastViewModel()
+    {
+        IsAstroModeEnabled = string.Equals(o as string, AstroCommandParameter, StringComparison.Ordinal),
+        SourceBitmap = image
+    };
--- a/PhotoLocator/MainWindow.xaml
+++ b/PhotoLocator/MainWindow.xaml
@@
-<KeyBinding Key="R" Modifiers="Ctrl" Command="{Binding JpegTransformCommands.LocalContrastCommand}" CommandParameter="Astro"/>
+<KeyBinding Key="R" Modifiers="Ctrl" Command="{Binding JpegTransformCommands.LocalContrastCommand}"
+            CommandParameter="{x:Static local:JpegTransformCommands.AstroCommandParameter}"/>

@@
-<MenuItem Header="Astrophoto stretch (Ctrl+R)" Command="{Binding JpegTransformCommands.LocalContrastCommand}" CommandParameter="Astro">
+<MenuItem Header="Astrophoto stretch (Ctrl+R)" Command="{Binding JpegTransformCommands.LocalContrastCommand}"
+          CommandParameter="{x:Static local:JpegTransformCommands.AstroCommandParameter}">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@PhotoLocator/JpegTransformCommands.cs` at line 106, The code in
JpegTransformCommands.cs constructs LocalContrastViewModel with
IsAstroModeEnabled = o as string == "Astro", which hardcodes the "Astro"
command; replace that literal with a shared constant (e.g. add a public const
string Astro = "Astro" in a Commands or Constants class and use Commands.Astro
here) and update any other places (including XAML bindings that reference the
literal) to reference the same constant (use x:Static in XAML if applicable) so
renames remain safe; change the equality check in the JpegTransformCommands
method and any other comparisons to use the new constant instead of the string
literal.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@PhotoLocator/BitmapOperations/AstroStretchOperation.cs`:
- Around line 46-48: The computed sampled width can evaluate to 0 for very
tall/narrow images causing an empty FloatBitmap and downstream divide-by-zero in
Mean(); update the resize code in AstroStretchOperation (around
ConvertToGrayscaleOperation.ConvertToGrayscale, FloatBitmap creation and
BilinearResizeOperation.ApplyToPlaneParallel) to compute a sampledWidth and
clamp it to a minimum of 1 (e.g. sampledWidth = Math.Max(1, calculatedWidth)
using an appropriate rounding), then construct the FloatBitmap with that clamped
width and SampleHeight before calling
BilinearResizeOperation.ApplyToPlaneParallel.

In `@PhotoLocator/LocalContrastViewModel.cs`:
- Around line 100-106: The IsAstroModeEnabled setter only raises PropertyChanged
for itself via SetProperty(ref field, value) but neglects to notify the
dependent IsLocalAdjustmentsLabelVisible and to refresh the preview pipeline;
update the setter in IsAstroModeEnabled so that after a successful SetProperty
it also calls OnPropertyChanged(nameof(IsLocalAdjustmentsLabelVisible)) (or
SetProperty overload that notifies both) and then triggers the pipeline/preview
refresh by calling the existing refresh method used elsewhere (e.g.,
SchedulePipelineRefresh(), RefreshPreview() or equivalent) so toggling astro
mode immediately updates the label and preview.

In `@PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs`:
- Line 32: The test AstroStretchOperationTest currently asserts
Assert.AreEqual(0.0026545193517195226, op.DstBitmap.Mean(), 0.000001) which is
too tight for JPEG/floating-point variations; change the assertion to use a
looser tolerance (for example 1e-4 or 1e-5) or assert the mean falls within an
explicit range (e.g., Assert.IsTrue(Math.Abs(op.DstBitmap.Mean() -
0.0026545193517195226) < 1e-4) or Assert.That(op.DstBitmap.Mean(),
Is.InRange(expected - delta, expected + delta))) so the check on
op.DstBitmap.Mean() is robust across environments.

---

Outside diff comments:
In `@PhotoLocator/LocalContrastViewModel.cs`:
- Around line 573-589: The no-op fast path (IsNoOperation) currently ignores
astro-related settings so ApplyOperations can incorrectly skip astro-only edits;
update the IsNoOperation getter to also require astro mode and astro parameters
be at their defaults—e.g., include checks on IsAstroModeEnabled (or
_astroOperation.IsAstroModeEnabled), AstroStretch == DefaultAstroStretch,
BackgroundRemovalSmooth == DefaultBackgroundRemovalSmooth and BlackPoint ==
DefaultBlackPoint (or the corresponding properties/fields) so that any
non-default astro setting prevents returning the original source and forces the
astro pipeline (ApplyAstroStretchOperation and related steps) to run.

---

Nitpick comments:
In `@PhotoLocator/JpegTransformCommands.cs`:
- Line 106: The code in JpegTransformCommands.cs constructs
LocalContrastViewModel with IsAstroModeEnabled = o as string == "Astro", which
hardcodes the "Astro" command; replace that literal with a shared constant (e.g.
add a public const string Astro = "Astro" in a Commands or Constants class and
use Commands.Astro here) and update any other places (including XAML bindings
that reference the literal) to reference the same constant (use x:Static in XAML
if applicable) so renames remain safe; change the equality check in the
JpegTransformCommands method and any other comparisons to use the new constant
instead of the string literal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55be7359-d257-46ef-a696-0c201259d84c

📥 Commits

Reviewing files that changed from the base of the PR and between da158e6 and 5ee1a45.

📒 Files selected for processing (14)
  • PhotoLocator/BitmapOperations/AstroStretchOperation.cs
  • PhotoLocator/BitmapOperations/FloatBitmap.cs
  • PhotoLocator/Helpers/CelestialCalculator.cs
  • PhotoLocator/JpegTransformCommands.cs
  • PhotoLocator/LocalContrastView.xaml
  • PhotoLocator/LocalContrastViewModel.cs
  • PhotoLocator/MainViewModel.cs
  • PhotoLocator/MainWindow.xaml
  • PhotoLocator/MapDisplay/MapView.xaml
  • PhotoLocator/Metadata/ExifHandler.cs
  • PhotoLocator/Metadata/ExifTool.cs
  • PhotoLocator/PictureFileFormats/GeneralFileFormatHandler.cs
  • PhotoLocator/PictureItemViewModel.cs
  • PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs

Comment thread PhotoLocator/BitmapOperations/AstroStretchOperation.cs
Comment thread PhotoLocator/LocalContrastViewModel.cs
Comment thread PhotoLocatorTest/BitmapOperations/AstroStretchOperationTest.cs Outdated
@meesoft meesoft merged commit 2867859 into main May 10, 2026
5 checks passed
@meesoft meesoft deleted the features/AstroStretch branch May 10, 2026 14:06
@coderabbitai coderabbitai Bot mentioned this pull request May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant